Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding framebuffer support for filter() + CreateFilterShader for 2D mode #6559

Merged
merged 27 commits into from
Nov 23, 2023

Conversation

perminder-17
Copy link
Contributor

@perminder-17 perminder-17 commented Nov 16, 2023

Resolves #6521 #6520

Changes:

Screenshots of the change:

20231116162522.mp4

PR Checklist

@perminder-17
Copy link
Contributor Author

perminder-17 commented Nov 16, 2023

@davepagurek the code is working for all three cases (createGraphics, createFramebuffer, and normal). The yScale of the camera was inverted when called inside the framebuffer, so I adjusted the yScale with curCamera. Let me know if there's a chance this code might break. Also, I'm not sure if resizing the canvas the way I did is correct for framebuffers.

@perminder-17
Copy link
Contributor Author

UPDATED BUILD OF THE CODE FOR CHECKING PURPOSE
https://editor.p5js.org/aman12345/sketches/gYH9Q4CJm

@davepagurek
Copy link
Contributor

This is looking good so far!

Some remaining tasks:

  • It looks like filter(BLUR) still breaks
  • I was trying to test createFilterShader. It seems to work in WebGL mode, but I forgot that this PR has not yet been merged to make sure it works in 2D mode: createFilterShader docs + fixes #6530 I'm going to see if I can get that in soon, but in the mean time, it might be worth merging that into your branch too so you can make sure filter shaders still work correctly in 2D canvases

filter(...args) {

// Treating 'pg' as a framebuffer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can rename this from pg? I think pg is a holdover from Processing where and the PGraphics class, whose p5 equivalent is p5.Graphics. Now that we're using a framebuffer, it might be less confusing to call this something else (often in p5 and in other Web/OpenGL code you use fbo, for "framebuffer object")


// Set the yScale of the filterCamera for framebuffers.
if (target !== this) {
this.filterCamera.yScale = this._curCamera.yScale;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're always using the filterCamera on a framebuffer now so I think its yScale should always be -1 (which is what your logic currently does as well.) But maybe a simpler way of achieving the same thing would be, when we define this.filterCamera, to say this.filterCamera = this.getFilterGraphicsLayer().createCamera() -- that way, its size will always match the size of the filter graphic when it resizes, and we won't need to touch its yScale value since it will be initialized to the correct value.

filter(...args) {

// Treating 'pg' as a framebuffer.
let pg = this.getFilterGraphicsLayer();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should rename this and getFilterGraphicsLayerTemp to not mention Graphics any more? Maybe just getFilterLayer and getFilterLayerTemp?

@perminder-17
Copy link
Contributor Author

perminder-17 commented Nov 21, 2023

  • still

This is looking good so far!

Some remaining tasks:

  • It looks like filter(BLUR) still breaks
  • I was trying to test createFilterShader. It seems to work in WebGL mode, but I forgot that this PR has not yet been merged to make sure it works in 2D mode: createFilterShader docs + fixes #6530 I'm going to see if I can get that in soon, but in the mean time, it might be worth merging that into your branch too so you can make sure filter shaders still work correctly in 2D canvases

Okay, @davepagurek can you please provide me the code where the filter(BLUR) is brekaing?? I will try to figure it out

this.filterShader.setUniform('tex0', tmp);
pg.clear();
pg.rect(-this.width / 2, -this.height / 2, this.width, this.height);
tmp.draw(() =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now this code is nested, which is causing this error and making it not render:

WebGL warning: drawElementsInstanced: Texture level 0 would be read by TEXTURE_2D unit 0, but written by framebuffer attachment COLOR_ATTACHMENT0, which would be illegal feedback.

I think a simplified order, without nesting and without extra copying of data, could be:

this.filterShader.setUniform('texelSize', texelSize);
this.filterShader.setUniform('canvasSize', [target.width, target.height]);
this.filterShader.setUniform('radius', Math.max(1, filterParameter));

// Horiz pass: draw `target` to `tmp`
tmp.draw(() => {
  this.filterShader.setUniform('direction', [1, 0]);
  this.filterShader.setUniform('tex0', target);
  this._pInst.clear();
  this._pInst.shader(this.filterShader);
  this._pInst.rect(-target.width / 2,
    -target.height / 2, target.width, target.height);
});

// Vert pass: draw `tmp` to `pg`
pg.draw(() => {
  this.filterShader.setUniform('direction', [0, 1]);
  this.filterShader.setUniform('tex0', tmp);
  this._pInst.clear();
  this._pInst.shader(this.filterShader);
  this._pInst.rect(-target.width / 2,
    -target.height / 2, target.width, target.height);
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testing, I just changed filter(INVERT) to filter(BLUR) in your latest p5 editor link.

Copy link
Contributor Author

@perminder-17 perminder-17 Nov 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @davepagurek if you come across any issues with the filter (blur), I also tested it and didn't find it breaking. but I've committed the changes, and it seems like the code is now more readable than the nested one. Thanks for your understanding

blur with nested one...from the above editor. is this not the expected behaviour??

Screenshot 2023-11-21 205252

sorry if i did'nt understood where the bug is happening.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it was a browser-specific issue? in Firefox I was seeing no output on the canvas. Anyway in your most recent build it seems to be working!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, got it @davepagurek

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we no longer need this line now that we pass target in as a uniform below

@perminder-17
Copy link
Contributor Author

bluild of the code after the changes;-

https://editor.p5js.org/aman12345/sketches/WQl9tDMOs

@perminder-17
Copy link
Contributor Author

perminder-17 commented Nov 21, 2023

This is looking good so far!

Some remaining tasks:

  • It looks like filter(BLUR) still breaks
  • I was trying to test createFilterShader. It seems to work in WebGL mode, but I forgot that this PR has not yet been merged to make sure it works in 2D mode: createFilterShader docs + fixes #6530 I'm going to see if I can get that in soon, but in the mean time, it might be worth merging that into your branch too so you can make sure filter shaders still work correctly in 2D canvases

For this createFilterShader docs + fixes PR, should I copy all the changes into my branch and try running it locally to see whether it breaks for 2D or not without commiting any changes here? Or something else I need to do for this? If this breaks and something needs to be changed here in this PR createFilterShader docs + fixes PR then what steps should be taken??

fbo.height !== this.height
) {
// Resize fbo
fbo.resizeCanvas(this.width, this.height);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we'll have to do all this for tmp too. Maybe it's worth putting this into a function, something like matchSize(fboToMatch, target) so that you can call it on fbo here and also on tmp later?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

@davepagurek
Copy link
Contributor

For this createFilterShader docs + fixes PR, should I copy all the changes into my branch and try running it locally to see whether it breaks for 2D or not without commiting any changes here? Or something else I need to do for this? If this breaks and something needs to be changed here in this PR createFilterShader docs + fixes PR then what steps should be taken??

Maybe the fastest way forward is to merge those changes into your branch and get them working with your branch? And then if they're working here, I can just close my PR, since all the updates will be in this one too.

@perminder-17
Copy link
Contributor Author

here's the build after merging @davepagurek https://editor.p5js.org/aman12345/sketches/Rbz3lXYlA

@perminder-17
Copy link
Contributor Author

If anything else needs to be changed, then please let me know @davepagurek .

@@ -1,7 +0,0 @@
function setup() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this intentionally deleted? Otherwise I think we can keep this file around

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh... Sorry

this.clear();
this._pInst.push();
this.filterCamera._resize();
this._pInst.setCamera(this.filterCamera);
this._pInst.resetMatrix();
this._pInst.image(pg, -this.width / 2, -this.height / 2,
this._pInst.image(fbo, -this.width / 2, -this.height / 2,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need these two lines above this call:

this._pInst.imageMode(constants.CORNER);
this._pInst.blendMode(constants.BLEND);

Currently, if you change the image mode or blend mode, the filter gets drawn with an offset: https://editor.p5js.org/davepagurek/sketches/q442qjF_1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

@perminder-17
Copy link
Contributor Author

perminder-17 commented Nov 23, 2023

I have a small question, @davepagurek . In this test, we're using WEBGL for this filter? Shouldn't we change the name from filterGraphicsLayer tofilterLayer here too? However, when I make this change, the tests fail.

test('filter() uses WEBGL implementation behind main P2D canvas', function() {
      let renderer = myp5.createCanvas(3,3);
      myp5.filter(myp5.BLUR);
      assert.isDefined(renderer.filterGraphicsLayer);
    });

@davepagurek
Copy link
Contributor

Only RendererGL has filterLayer I think, Renderer2D still has filterGraphicsLayer, which is a WebGL graphic. While WebGL mode can natively run shaders and can therefore just use a framebuffer directly, 2D mode can't run shaders, so it needs the extra step of making a WebGL graphic to run the filter on.

So:

  • In 2D mode, renderer.filterGraphicsLayer is a graphic, and renderer.filterGraphicsLayer.filterLayer is a framebuffer
  • In WebGL mode, renderer.filterLayer is a framebuffer and no graphic is involved

All that to say, I think that test is correct as it is.

@perminder-17
Copy link
Contributor Author

Only RendererGL has filterLayer I think, Renderer2D still has filterGraphicsLayer, which is a WebGL graphic. While WebGL mode can natively run shaders and can therefore just use a framebuffer directly, 2D mode can't run shaders, so it needs the extra step of making a WebGL graphic to run the filter on.

So:

  • In 2D mode, renderer.filterGraphicsLayer is a graphic, and renderer.filterGraphicsLayer.filterLayer is a framebuffer
  • In WebGL mode, renderer.filterLayer is a framebuffer and no graphic is involved

All that to say, I think that test is correct as it is.

Ooh....I was having a little confusion with this only. Thanks for your explanation.

);
}

return this.filterGraphicsLayer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this, we might want to do a similar size matching to what we do in RendererGL with its framebuffer so that we ensure this.filterGraphicsLayer matches width, height, and pixel density to this._pInst. Right now filters on 2D mode don't seem to work when resizing: https://editor.p5js.org/davepagurek/sketches/OOKkLOINp

Maybe we can also copy and paste the current unit test that checks that the framebuffer gets resized in WebGL mode, and adapt it to check the size of filterGraphicsLayer in 2D mode? It would definitely be helpful to leave behind as many tests as we can so that we don't have to rely so much on thinking of new things to test the next time we make changes to this system.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, i will do the changes asap.

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw I just wanted to say thanks for your patience and continued work on this as we work through all the bugs! Hopefully it will be easier for future contributors the more tests we add, so they can rely more on those tests to let them know that they've fixed everything.

@perminder-17
Copy link
Contributor Author

btw I just wanted to say thanks for your patience and continued work on this as we work through all the bugs! Hopefully it will be easier for future contributors the more tests we add, so they can rely more on those tests to let them know that they've fixed everything.

btw i need to thankyou for your patience and support. Love to have a mentor like you.

@Qianqianye Qianqianye added this to the 1.9.0 milestone Nov 23, 2023
@Qianqianye Qianqianye mentioned this pull request Nov 23, 2023
7 tasks
this.filterGraphicsLayer.height !== this.height
) {
// Resize the graphics layer
this.filterGraphicsLayer.resize(this.width, this.height);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case, since filterGraphicsLayer is a p5.Graphics, we can use resizeCanvas instead of resize. Unfortunately it's kinda confusing that the main canvas and graphics call it one thing but images and framebuffers call it something else :')

Copy link
Contributor Author

@perminder-17 perminder-17 Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops sorry, i commited it wrong file. sorry for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually take a look at the test for this. I am not good at writing tests.

let s = myp5.createShader(vert, frag);
myp5.filter(s);
myp5.resizeCanvas(5, 15);
assert.equal(g1.width, 5);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically we only care that the sizes are equal when we want to do a filter, so you might need to call myp5.filter(s) again before this assertion

@perminder-17
Copy link
Contributor Author

perminder-17 commented Nov 23, 2023

Are we doing the correct way for test? @davepagurek

@perminder-17
Copy link
Contributor Author

Let me know if we need something else @davepagurek ?

@davepagurek
Copy link
Contributor

I did a little testing and agree that everything is looking good now! I think the tests are looking good. Is there anything else you can think of that we might need to add before merging?

@perminder-17
Copy link
Contributor Author

I did a little testing and agree that everything is looking good now! I think the tests are looking good. Is there anything else you can think of that we might need to add before merging?

I will open an issue for covering all the test including filter and many more from this area. I don't think we are missing something with area of filter :)

@davepagurek
Copy link
Contributor

if everything also looks good to you then I think we're good to merge!

@perminder-17
Copy link
Contributor Author

perminder-17 commented Nov 23, 2023

if everything also looks good to you then I think we're good to merge!

I changed the heading of the issue? I think we are covering one more issue which we are merging (createFilterShader for 2D mode)

@perminder-17 perminder-17 changed the title Adding framebuffer support for filter() Adding framebuffer support for filter() + CreateFilterShader for 2D mode Nov 23, 2023
Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome, thanks for your work on this!

@davepagurek davepagurek merged commit c0acef8 into processing:main Nov 23, 2023
2 checks passed
@davepagurek davepagurek mentioned this pull request Nov 23, 2023
3 tasks
@perminder-17
Copy link
Contributor Author

awesome, thanks for your work on this!

Welcome. Love to contribute more like this❤

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Framebuffer support for filter()
3 participants